package com.aube.video.module;

import android.content.Context;
import android.view.ViewGroup;

import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.FileRequest;
import com.huyn.baseframework.dynamicload.DLProxyContext;
import com.huyn.baseframework.dynamicload.video.DLVideoLayout;
import com.huyn.baseframework.net.BFJSONRequest;
import com.huyn.baseframework.net.HttpRequestLoader;
import com.huyn.baseframework.net.OpenApi;
import com.huyn.baseframework.utils.Constant;
import com.huyn.baseframework.utils.FileMD5Verify;
import com.huyn.baseframework.utils.SharedPrefUtil;
import com.huyn.baseframework.utils.StorageUtil;
import com.huyn.baseframework.utils.StringUtils;
import com.huyn.baseframework.utils.Utils;
import com.ryg.dynamicload.internal.DLPluginManager;
import com.ryg.dynamicload.internal.DLPluginPackage;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.HashMap;

/**
 * Created by huyaonan on 16/11/1.
 */
public class PlayerModuleManager {

    private static final String TAG = "ModuleManager";

    private static PlayerModuleManager mInstance = null;
    private Context mContext;

    private HashMap<String, DLPluginPackage> mPluginMaps = new HashMap<>();

    private PlayerModuleManager(Context context) {
        mContext = context;
    }

    public static PlayerModuleManager getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new PlayerModuleManager(context.getApplicationContext());
        }
        return mInstance;
    }

    public void load(final Context context, final ViewGroup viewGroup, final String videoId, final OnAubePreparedListener listener) {
        // 1. create preparedListener.
        final OnModulePreparedListener preparedListener = new OnModulePreparedListener() {
            @Override
            public void onReady(DLPluginPackage pluginPackage, String packageName) {
                try {
                    if (listener != null)
                        listener.onPrepared(load(context, viewGroup, pluginPackage, packageName));
                } catch (Exception e) {
                    e.printStackTrace();
                    if (listener != null)
                        listener.onError(e);
                }
            }

            @Override
            public void onFail() {
                if(listener != null)
                    listener.onError(new Exception("module download fail"));
            }
        };

        // 2.1 get video theme if it was choosed in debug mode.
        final String videoTheme = SharedPrefUtil.getInstance(mContext).getString(SharedPrefUtil.VIDEO_THEME);
        if(videoTheme != null && StringUtils.isNotBlank(videoTheme) && !videoTheme.equalsIgnoreCase("Disable")) {
            HashMap<String, String> params = new HashMap<>();
            params.put("method", OpenApi.VIDEO_THEME_LIST_TEST);
            BFJSONRequest request = new BFJSONRequest(VideoThemeListModule.class, params, new Response.Listener<VideoThemeListModule>() {
                @Override
                public void onResponse(VideoThemeListModule response) {
                    if (response != null && response.success() && response.data != null) {
                        for(ModuleItem moduleItem : response.data) {
                            if(moduleItem.themeCode != null && moduleItem.themeCode.equalsIgnoreCase(videoTheme)) {
                                loadPluginFile(moduleItem, preparedListener);
                                return;
                            }
                        }

                        Utils.Log(TAG, "[getTestVideoTheme.onResponse] Failed: no matched theme. Try loading selected theme.");
                        // 3. get online selected video theme.
                        getModules(videoId, preparedListener);
                    } else {
                        Utils.Log(TAG, "[getTestVideoTheme.onResponse] Failed: response error. Try loading selected theme.");
                        // 3. get online selected video theme.
                        getModules(videoId, preparedListener);
                    }
                }

                @Override
                public void onErrorResponse(VolleyError error) {
                    Utils.Log(TAG, "[getTestVideoTheme.onErrorResponse] Failed.Try loading selected theme.");
                    // 3. get online selected video theme.
                    getModules(videoId, preparedListener);
                }

                @Override
                public void onStart() {
                }
            });
            HttpRequestLoader.getInstance(mContext).startHttpLoader(request);
        } else {
            // 2.2 get online selected video theme if so.
            getModules(videoId, preparedListener);
        }
    }

    public DLVideoLayout test(final Context context, final ViewGroup viewGroup) {

        return load(context, viewGroup, installPlugins("FamiliarTaste", "/storage/emulated/legacy/intera-taste-release-unsigned.apk"), "com.aube.interact.taste.AubePlayController");
//        return load(context, viewGroup, installPlugins("AndDefault", "/storage/emulated/0/test.apk"), "com.aube.interact.fighter.TestLayout");
    }

    private synchronized DLPluginPackage installPlugins(String themeCode, String path) {
        Utils.sysout("install......." + themeCode + " at: " + path);
        DLPluginPackage pkg = DLPluginManager.getInstance(mContext).loadApk(path, themeCode);
        mPluginMaps.put(themeCode, pkg);
        return pkg;
    }

    private DLVideoLayout load(Context context, ViewGroup viewGroup, DLPluginPackage pluginPackage, String packageName) {
//        DLVideoLayout layout = inflatePluginLayout(new DLProxyContext(context, pluginPackage), pluginPackage, packageName + ".AubeInteractionLayout");
        if (pluginPackage == null || StringUtils.isBlank(packageName)) {
            Utils.sysout("----------plugin is null");
            return null;
        } else
            Utils.sysout("----------plugin is not null");
        DLVideoLayout layout = inflatePluginLayout(new DLProxyContext(context, pluginPackage), pluginPackage, packageName);
        if(layout == null)
            return null;
        viewGroup.addView(layout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        return layout;
    }

    private void startPluginLoader(String url, String fileName, boolean canUseSDCard, Response.Listener<String> mRequestListener) {
        FileRequest request = new FileRequest(url, mRequestListener, null);

        String pkgName = mContext.getPackageName().replace(".", "_");
        request.setFileTitle(pkgName + "_" + toApk(fileName));
        request.setCanUseSDCard(canUseSDCard);
        HttpRequestLoader.getInstance(mContext).startHttpLoader(request);
    }

    public void release() {
        mInstance = null;
    }

    private void getModules(String videoId, final OnModulePreparedListener listener) {
        HashMap<String, String> params = new HashMap<>();
        params.put("method", OpenApi.VIDEO_THEME);
        params.put("videoid", videoId);
        BFJSONRequest request = new BFJSONRequest(ModuleData.class, params, new Response.Listener<ModuleData>() {
            @Override
            public void onResponse(ModuleData response) {
                if (response != null && response.success()) {
                    loadPluginFile(response.data, listener);
                } else {
                    if(listener != null)
                        listener.onFail();
                }
            }

            @Override
            public void onErrorResponse(VolleyError error) {
                if(listener != null)
                    listener.onFail();
            }

            @Override
            public void onStart() {

            }
        });
        HttpRequestLoader.getInstance(mContext).startHttpLoader(request);
    }

    private String toApk(String name) {
        if (name.contains(".apk"))
            return name;
        if (name.contains(".")) {
            return name.substring(0, name.lastIndexOf(".")) + ".apk";
        } else {
            return name + ".apk";
        }
    }

    private void loadPluginFile(ModuleItem item, OnModulePreparedListener listener) {
        try {
            String pkgName = mContext.getPackageName().replace(".", "_");
            String fileName = pkgName + "_" + toApk(item.getFileName());
            Utils.Log(TAG, "                                                        ..." + fileName);
            boolean canUseSDCard = StorageUtil.checkSDWritePermission(mContext);
            Utils.Log(TAG, "-----sd permission ..." + canUseSDCard);
            File destFile = BasicNetwork.newPluginFile(fileName, canUseSDCard);
            Utils.Log(TAG, "-----target file..." + destFile.getPath());
            if (destFile.exists()) {
                Utils.sysout("-----check file..." + item.getFileName());
                boolean verifyResult = FileMD5Verify.md5sum(destFile, item.md5);
                if (verifyResult) {
                    Utils.Log(TAG, "-----verify..." + fileName);
                    installPlugins(item, destFile.getPath(), listener);
                } else {
                    destFile.delete();
                    Utils.Log(TAG, "-----delete and start to download..." + fileName);
                    downloadPlugin(item, canUseSDCard, listener);
                }
            } else {
                if (!copyPluginFromAssets(item, listener)) {
                    Utils.Log(TAG, "load plugin..." + fileName + "___from network");
                    downloadPlugin(item, canUseSDCard, listener);
                } else {
                    Utils.Log(TAG, "-----copy from assets success..." + fileName);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (listener != null)
                listener.onFail();
        }
    }

    /**
     * 将episode_1.apk从assets文件夹拷贝到本地文件夹,同时文件名变为com_aube_xxx_episode_1.apk
     *
     * @param item
     * @return
     */
    private boolean copyPluginFromAssets(ModuleItem item, OnModulePreparedListener listener) {
        try {
            String pkgName = mContext.getPackageName().replace(".", "_");
            String fileName = pkgName + "_" + item.getFileName();
            InputStream is = mContext.getAssets().open(item.getFileName());
            if (is == null)
                return false;
            File destFile = BasicNetwork.newPluginFile(fileName);
            FileOutputStream fos = new FileOutputStream(destFile);
            byte[] buffer = new byte[1024];
            while (true) {
                int len = is.read(buffer);
                if (len == -1) {
                    break;
                }
                fos.write(buffer, 0, len);
            }
            is.close();
            fos.close();

            installPlugins(item, destFile.getPath(), listener);

            Utils.Log(TAG, "load plugin..." + fileName + "_from assets success");

            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private void downloadPlugin(final ModuleItem item, final boolean canUseSDCard, final OnModulePreparedListener listener) {
        startPluginLoader(item.download, item.getFileName(), canUseSDCard, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                boolean verifyResult = FileMD5Verify.md5sum(response, item.md5);

                Utils.Log(TAG, "plugin.." + item.packagename + " download success at:" + response + ", verify " + (verifyResult ? "success" : "fail"));

                if (verifyResult)
                    installPlugins(item, response, listener);
                else {
                    if(listener != null)
                        listener.onFail();
                }
            }

            @Override
            public void onErrorResponse(VolleyError error) {
                if(listener != null)
                    listener.onFail();
            }

            @Override
            public void onStart() {

            }
        });
    }

    /**
     * Collect plugin here
     * @param item
     * @param path
     * @param listener
     */
    private synchronized void installPlugins(ModuleItem item, String path, OnModulePreparedListener listener) {
        Utils.sysout("install......." + item.themeCode + " at: " + path);
        DLPluginPackage pkg = DLPluginManager.getInstance(mContext).loadApk(path, item.themeCode);
        mPluginMaps.put(item.themeCode, pkg);
        listener.onReady(pkg, item.packagename);
    }

    /**
     * Inflate constructor instance
     * @param context
     * @param plgPgk
     * @param clazzName
     * @return
     */
    private DLVideoLayout inflatePluginLayout(Context context, DLPluginPackage plgPgk, String clazzName) {
        try {
            Utils.sysout("---------------------classloader:" + plgPgk.classLoader);
            Class<?> localClass = plgPgk.classLoader.loadClass(clazzName);
            Constructor<?> localConstructor = localClass.getConstructor(new Class[]{Context.class});
            DLVideoLayout instance = (DLVideoLayout) localConstructor.newInstance(new Object[]{context});
            instance.injectPluginPackage(plgPgk);
            return instance;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
